package com.zimmur.controller;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.zimmur.alipay.aliconfig.AlipayMethodEnum;
import com.zimmur.alipay.util.AlipayClientUtil;
import com.zimmur.alipay.util.AlipayNotify;
import com.zimmur.common.ReturnJsonEntity;
import com.zimmur.common.StatusCodeEnum;
import com.zimmur.model.order.Order;
import com.zimmur.model.order.WxPayLog;
import com.zimmur.model.wxpay.AlipayNotifyLog;
import com.zimmur.service.account.IPrepaidLogService;
import com.zimmur.service.order.IOrderService;
import com.zimmur.service.order.IWxPayLogService;
import com.zimmur.service.paynotify.IAlipayNotifyLogService;
import com.zimmur.tenpay.util.WechatUtil;
import com.zimmur.tenpay.util.XMLUtil;
import com.zimmur.tools.DateUtils;
import com.zimmur.tools.XmlConverUtil;


/**
* <p>Title: PayController</p>
* <p>Description:订单支付处理 </p>
* <p>Company: zimmur</p>
* <p>Copyright: Copyright (c) 2017</p>
* @author huangsx
* @version 1.0
* @time 2018年1月9日 下午8:37:54
 */
@RestController
@RequestMapping(value = "/restful/pay")
public class PayController extends BaseController {
	private static Logger log = LoggerFactory.getLogger(PayController.class);

	@Resource(name = "orderServiceImpl")
	private IOrderService orderService;
	@Resource
	private IWxPayLogService wxPayLogService;
	@Resource
	private IAlipayNotifyLogService alipayNotifyLogService;
	@Resource
	private IPrepaidLogService prepaidLogService;
	@Resource(name = "taskExecutor")
	private ThreadPoolTaskExecutor taskExecutor;

	@Value("#{spingMvcProperties['WX.APPID']}")
	private String wxAppId;

	@Value("#{spingMvcProperties['WX.GATEURL']}")
	private String gateUrl;

	@Value("#{spingMvcProperties['WX.PARTNER']}")
	private String partnerId;

	@Value("#{spingMvcProperties['WX.KEY']}")
	private String wxKey;

	@Value("#{spingMvcProperties['WX.NOTIFY_URL']}")
	private String notifyUrl;

	@Value("#{spingMvcProperties['System.GuestId']}")
	private String guessUserId;
	
	private static String CHARSET="utf-8";
	@InitBinder
	public void initbinder() {
		WechatUtil.setWxParameter(wxAppId, wxKey, gateUrl, partnerId, notifyUrl);
	}
	/**
	* <p>Title: toPay</p>
	* <p>Description:支付 </p>
	* @return
	 */
	@RequestMapping(value="/topay",method = RequestMethod.POST)
	public ReturnJsonEntity toPay() {
		ReturnJsonEntity returnJsonEntity = new ReturnJsonEntity();
		StatusCodeEnum statusCodeEnum = StatusCodeEnum.CODE100000;
		Map<String,Object> backMap = new HashMap<String, Object>();
		//游客身份验证
		if(String.valueOf(getTokenUserId()).equals(guessUserId)){
			statusCodeEnum=StatusCodeEnum.CODE100200;
			returnJsonEntity.setCode(statusCodeEnum.getCode());
			returnJsonEntity.setMsg(statusCodeEnum.getMsg());
			return returnJsonEntity;
		}
		Long orderId=getLongParam("orderId");
		String source = getParam("source");//来源：APP=手机App
		String payment = getParam("payment");//支付方式：alipay = 支付宝支付，wxpay=微信支付
		Order order=orderService.selectByOrderId(orderId);
		if(order!=null){
			Long trade_no=Long.parseLong(order.getOrderId());
			String order_je = String .format("%.2f",order.getOrderJe());
			String subject="POPOLOOK";
			//支付宝支付
			if (!StringUtils.isEmpty(payment) && payment.equals("alipay")) {
				try {
					String form = AlipayClientUtil.toPay(trade_no,order_je,subject,"",AlipayMethodEnum.valueOf(source));
					backMap.put("form", form);
					backMap.put("source", source);
					backMap.put("payment", payment);
					statusCodeEnum = StatusCodeEnum.CODE000000;
					returnJsonEntity.setCode(statusCodeEnum.getCode());
					returnJsonEntity.setMsg(statusCodeEnum.getMsg());
					returnJsonEntity.setData(backMap);
					return returnJsonEntity;
				} catch (Exception e) {
					log.error(e.getMessage(),e);
				}
			}else{
			/**
			 * 微信支付常用的三种支付方式
			 * 1.app支付
			 * 2.PC生成二维码支付
			 * 3.公众号支付
			 * 4.H5支付
			 */
				if(!StringUtils.isEmpty(source)){
					if(source.equals("APP")){
						//1.调用微信支付系统生成预支付id以及客户端支付信息
						returnJsonEntity = createPrePayId("APP");
						backMap = (Map<String, Object>) returnJsonEntity.getData();
						backMap.put("source", source);
						backMap.put("payment", "wxpay");
						returnJsonEntity.setCode(returnJsonEntity.getCode());
						returnJsonEntity.setMsg(returnJsonEntity.getMsg());
						return returnJsonEntity;
					}else if(source.equals("PC")){
						//1.调用统一下单接口
						returnJsonEntity = createPrePayId("NATIVE");
						Map<String,Object> returnMap = (Map<String, Object>) returnJsonEntity.getData();
						String codeUrl = returnMap.get("payPackage").toString();
						if(!"".equals(codeUrl)){
							statusCodeEnum = StatusCodeEnum.CODE000000;
							backMap.put("qrImage", codeUrl);
							backMap.put("source", source);
							backMap.put("payment", "wxpay");
							backMap.put("orderId", orderId+"");//返回订单id
							returnJsonEntity.setData(backMap);
							returnJsonEntity.setCode(statusCodeEnum.getCode());
							returnJsonEntity.setMsg(statusCodeEnum.getMsg());
						}else{
							returnJsonEntity.setCode(statusCodeEnum.getCode());
							returnJsonEntity.setMsg(statusCodeEnum.getMsg());
						}
					}else if(source.equals("H5")){
						//1.调用统一下单接口
						returnJsonEntity = createPrePayId("MWEB");
						Map<String,Object> returnMap = (Map<String, Object>) returnJsonEntity.getData();
						String codeUrl = returnMap.get("payPackage").toString();
						if(!"".equals(codeUrl)){
							log.info("H5支付的codeUrl:"+codeUrl);
							statusCodeEnum = StatusCodeEnum.CODE000000;
							backMap.put("qrImage", codeUrl);
							backMap.put("source", source);
							backMap.put("payment", "wxpay");
							backMap.put("orderId", orderId+"");//返回订单id
							returnJsonEntity.setData(backMap);
							returnJsonEntity.setCode(statusCodeEnum.getCode());
							returnJsonEntity.setMsg(statusCodeEnum.getMsg());
						}else{
							returnJsonEntity.setCode(statusCodeEnum.getCode());
							returnJsonEntity.setMsg(statusCodeEnum.getMsg());
						}
					}
				}
			}
		}
		return returnJsonEntity;
	}
	
	/**
	* <p>Title: toalipay</p>
	* <p>Description: 支付宝支付</p>
	* @return
	 */
	@RequestMapping(value="/toalipay",method = RequestMethod.POST)
	public void toAlipay() {
		Long orderId=getLongParam("orderId");
		String payment = getParam("payment");//支付方式：alipay = 支付宝支付
		try {
			//游客身份验证
			if(String.valueOf(getTokenUserId()).equals(guessUserId)){
				 getResponse().setContentType("text/html;charset=" + CHARSET);
				 getResponse().getWriter().write("游客身份不能进行支付操作");
				 getResponse().getWriter().flush();
			}else{
				Order order=orderService.selectByOrderId(orderId);
				if(order!=null){
					Long trade_no=Long.parseLong(order.getOrderId());
					String order_je = String .format("%.2f",order.getOrderJe());
					String subject="POPOLOOK";
					//支付宝支付
					if(!StringUtils.isEmpty(payment)&&payment.equals("alipay")){
						//直接将完整的表单html输出到页面
						String form = AlipayClientUtil.toPay(trade_no,order_je,subject,"",AlipayMethodEnum.PC);
						getResponse().setContentType("text/html;charset=" + CHARSET);
						getResponse().getWriter().write(form);
						getResponse().getWriter().flush();
					}
				}else{
					    getResponse().setContentType("text/html;charset=" + CHARSET);
						getResponse().getWriter().write("订单不存在,支付失败");
						getResponse().getWriter().flush();
				}
			}
		} catch (Exception e) {
			log.error(e.getMessage(),e);
		}
	}
	/**
	 * 微信支付
	 */
	private ReturnJsonEntity createPrePayId(String tradeType) {
		ReturnJsonEntity returnJsonEntity = new ReturnJsonEntity();
		StatusCodeEnum statusCodeEnum = StatusCodeEnum.CODE100000;
		String orderId = getParam("orderId");
		try {
			if (!"".equals(orderId)) {
				double orderJe = 0;
				Map<String, Object> map = new HashMap<String, Object>();
				map.put("orderId", orderId);
				Order orderData = orderService.selectByOrderId(Long.parseLong(orderId));
				if (orderData != null) {
					orderJe = orderData.getOrderJe();
				}
				if (orderJe > 0) {
					map.put("orderJe", orderJe);
					//调用微信统一下单接口，生成预支付id
					ReturnJsonEntity returnWx = callWxPrePay(orderId, orderJe,tradeType);
					if (returnWx.getCode().equals(StatusCodeEnum.CODE000000.getCode())) {
						statusCodeEnum = StatusCodeEnum.CODE000000;
						map.put("payPackage", returnWx.getData());
						returnJsonEntity.setCode(statusCodeEnum.getCode());
						returnJsonEntity.setMsg(statusCodeEnum.getMsg());
					} else {
						map.put("payPackage", new HashMap());
						returnJsonEntity.setMsg(returnWx.getMsg());
					}

					returnJsonEntity.setData(map);
				}
			}

		} catch (Exception e) {
			log.error(orderId + "生成微信预支付ID错误:" + e.getMessage());
		}
		returnJsonEntity.setCode(statusCodeEnum.getCode());

		return returnJsonEntity;
	}
	/**
	* <p>Title: callWxPrePay</p>
	* <p>Description: 发起微信预支付</p>
	* @param orderId
	* @param orderJe
	* @return
	 */
	private ReturnJsonEntity callWxPrePay(String orderId, double orderJe,String tradeType) {
		ReturnJsonEntity returnJsonEntity = new ReturnJsonEntity();
		StatusCodeEnum statusCodeEnum = StatusCodeEnum.CODE100000;
		try {
			try {
				String codeUrl="";
				String body = "POPOLOOK" + orderId;
				int totalFee = (int) (orderJe * 100);
				String ip = getRemoteIp();
				String productId = String.valueOf(System.currentTimeMillis());
				String sceneInfo ="{\"h5_info\":{\"type\":\"Wap\",\"wap_url\":\"http://www.popolook.com\",\"wap_name\":\"POPOLOOK手机Wap\"}}";
				Map<String, String> map = WechatUtil.getPayPreId(orderId, body,productId, notifyUrl, ip,
						String.valueOf(totalFee),tradeType,sceneInfo);
				if (map.get("return_code").equals("SUCCESS") && map.get("prepay_id") != null) {
					//APP支付：取返回的预支付id
					if(tradeType.equals("APP")){
						String prePayId = map.get("prepay_id");
						//将预支付id进行封装map返回给商户app客户端
						Map<String, String> mapAppPayPackage = WechatUtil.genPayReq(prePayId);
						returnJsonEntity.setData(mapAppPayPackage);
					}
					//微信扫码支付：取返回code_url用于生成生成支付的二维码
					if(tradeType.equals("NATIVE")){
						codeUrl = map.get("code_url");
						returnJsonEntity.setData(codeUrl);
					}
					//H5支付:返回mweb_url,用于拉起微信客户端进行微信支付
					if(tradeType.equals("MWEB")){
						codeUrl = map.get("mweb_url");
						returnJsonEntity.setData(codeUrl);
					}
					statusCodeEnum = StatusCodeEnum.CODE000000;
					returnJsonEntity.setMsg(statusCodeEnum.getMsg());
					try {
						savePrePayLog(wxAppId, String.valueOf(orderId), body, notifyUrl, ip, totalFee, tradeType,
								map.get("return_code"), map.get("return_msg"), "",codeUrl);
					} catch (Exception er) {
						log.error("savePrePayLog错误:" + er.getMessage());
					}
				} else {
					returnJsonEntity.setMsg(map.get("return_msg"));
					savePrePayLog(wxAppId, String.valueOf(orderId), body, notifyUrl, ip, totalFee, tradeType,
							map.get("return_code"), map.get("return_msg"), "","");
				}
				map = null;
			} catch (Exception e) {
				statusCodeEnum = StatusCodeEnum.CODE100000;
				returnJsonEntity.setMsg(statusCodeEnum.getMsg());
				log.error(orderId + "生成微信预支付ID错误:" + e.getMessage());
			}
		} catch (Exception e) {
			log.error("callWxPrePay error:" + e.getMessage());
		}
		returnJsonEntity.setCode(statusCodeEnum.getCode());

		return returnJsonEntity;
	}
	/**
	* <p>Title: savePrePayLog</p>
	* <p>Description: 商户后台系统：保存发起预支付信息</p>
	* @param appid
	* @param goodOrderNo 订单号
	* @param body  商品备注
	* @param noticeUrl 通知地址
	* @param ip
	* @param totalFee
	* @param tradeType 交易类型
	* @param returnCode
	* @param returnMsg
	* @param prePayId  预支付ID
	 */
	private void savePrePayLog(String appid, String goodOrderNo, String body, String noticeUrl, String ip, int totalFee,
			String tradeType, String returnCode, String returnMsg, String prePayId,String qrImage) {
		WxPayLog payLog = new WxPayLog();
		payLog.setCreatedate(DateUtils.getNowUnixTime());
		payLog.setAppid(appid);
		payLog.setBody(body);
		payLog.setOutTradeNo(goodOrderNo);
		payLog.setTotalFee(totalFee);
		payLog.setSpbillCreateIp(ip);
		payLog.setNotifyUrl(noticeUrl);
		payLog.setTradeType(tradeType);
		payLog.setReturnCode(returnCode);
		payLog.setReturnMsg(returnMsg);
		payLog.setPrepayId(prePayId);
		payLog.setResultCode("");
		payLog.setQrImage(qrImage);
		payLog.setIsFinish((short) 0);
		wxPayLogService.insertLog(payLog);
	}

	/**
	* <p>Title: saveWxNotify</p>
	* <p>Description: wx支付通知处理</p>
	* @param map
	* @return
	 */
	private String saveWxNotify(final Map<String, String> map) {
		try {
			String resultCode = map.get("return_code");
			String sign = map.get("sign");
//			log.info("saveWxNotify:" + resultCode + " sign:" + sign);
			// 如果wechatInfoEntity存在，说明请求已经处理过，直接返回
			if (WechatUtil.getSignVeryfy(map, sign)) {
				String errCode = "";
				String errCodeDes = "";
				String openId = "", isSubscribe = "", bankType = "", feeType = "", transactionId = "", orderNo = "",
						cashFeeType = "", timeEnd = "";
				int total_fee = 0, cashFee = 0, couponFee = 0, couponCount = 0;
				openId = map.get("openid");
				// 商户订单号
				orderNo = map.get("out_trade_no");
				// 财付通订单号
				transactionId = map.get("transaction_id");
				bankType = map.get("bank_type");
				feeType = map.get("fee_type");
				isSubscribe = map.get("is_subscribe");

				// 金额,以分为单位
				total_fee = Integer.parseInt(map.get("total_fee"));
				cashFee = Integer.parseInt(map.get("cash_fee"));
				// 如果有使用折扣券，discount有值，total_fee+discount=原请求的total_fee
				String discount = map.get("discount");
				timeEnd = map.get("time_end");
				// 交易成功
				if (!"SUCCESS".equals(resultCode)) {
					errCode = map.get("err_code");
					errCodeDes = map.get("err_code_des");
					// 错误时，返回结果未签名，记录retcode、retmsg看失败详情。
					// log.error("retcode:" + errCode + " retmsg:" +
					// errCodeDes);
				}
				int notifydate = DateUtils.getNowUnixTime();
				wxPayLogService.updateNotifyPayStatus(resultCode, errCode, errCodeDes, openId, isSubscribe, orderNo,
						notifydate, bankType, total_fee, cashFee, cashFeeType, couponFee, couponCount, transactionId,
						timeEnd);
				return "SUCCESS";
			} else {
				log.info("fail -Md5 failed");
			}
		} catch (Exception er) {
			log.error(er.getMessage(), er);
		}
		return "FAIL";
	}
	@ResponseBody
	@RequestMapping(value = "/alipay/notify")
	public String alipayNotify() {
		String retStr = "success";
		final HttpServletRequest request = getRequest();
		taskExecutor.submit(new Runnable() {
			@Override
			public void run() {
				// 获取支付宝POST过来反馈信息
				Map<String, String> params = new HashMap<String, String>();
				Map requestParams = request.getParameterMap();
				try {
					for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
						String name = (String) iter.next();
						String[] values = (String[]) requestParams.get(name);
						String valueStr = "";
						for (int i = 0; i < values.length; i++) {
							valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
						}
						// 乱码解决，这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
						// valueStr = new
						// String(valueStr.getBytes("ISO-8859-1"), "gbk");
						params.put(name, valueStr);
					}
				} catch (Exception e) {
					log.error(e.getMessage(), e);
				}
				// 交易状态
				try {
					// String tradeStatus = new
					// String(getParam("trade_status").getBytes("ISO-8859-1"),"UTF-8");
					if (!params.isEmpty() && AlipayNotify.verify(params)) {// 验证成功
						String orderId = params.get("out_trade_no");
						if (orderId == null || orderId.equals("")){
							return;
						}
						String tradeStatus = params.get("trade_status");
						String notifyTime = params.get("notify_time");
						String notifyType = params.get("notify_type");
						String notifyId = params.get("notify_id");
						String paymentType = params.get("payment_type");
						String tradeNo = params.get("trade_no");
						String sellerId = params.get("seller_id");
						String sellerEmail = params.get("seller_email");
						String buyerId = params.get("buyer_id");
						String buyerEmail = params.get("buyer_email");
						String totalFee = params.get("buyer_pay_amount");
						if (totalFee == null || totalFee.equals(""))
							totalFee = "0";
						String quantity = params.get("quantity");
						if (quantity == null || quantity.equals(""))
							quantity = "1";
						AlipayNotifyLog logModel = new AlipayNotifyLog();
						logModel.setTradeStatus(tradeStatus);
						logModel.setNotifyTime(notifyTime);
						logModel.setNotifyType(notifyType);
						logModel.setNotifyId(notifyId);
						logModel.setPaymentType(paymentType);
						logModel.setOutTradeNo(orderId);
						logModel.setTradeNo(tradeNo);
						logModel.setSellerId(sellerId);
						logModel.setSellerEmail(sellerEmail);
						logModel.setBuyerId(buyerId);
						logModel.setBuyerEmail(buyerEmail);
						logModel.setTotalFee(Double.parseDouble(totalFee));
						logModel.setQuantity(Integer.parseInt(quantity));
						logModel.setCreateDate(DateUtils.getNowUnixTime());
						alipayNotifyLogService.addNotifyLog(logModel);
					} else {
						log.error("alipay notify verify error:" + params.toString());
					}
				} catch (Exception e) {
					log.error(e.getMessage(), e);
				}
			}
		});

		return retStr;
	}
	/**
	* <p>Title: wxPayNotify</p>
	* <p>Description:接收微信支付后的通知接口 </p>
	* @return
	 */
	@ResponseBody
	@RequestMapping(value = "/wxpay/notify")
	public String wxPayNotify(HttpServletRequest request) {
		log.info("~~~~~~~~~~~~~~~~付款成功通知~~~~~~~~~");
		String result = "";
		InputStream inStream = null;
		ByteArrayOutputStream outSteam = null;
		try{
			inStream = request.getInputStream();
			outSteam = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			int len = 0;
			while ((len = inStream.read(buffer)) != -1) {
				outSteam.write(buffer, 0, len);
			}
			buffer = null;
			outSteam.flush();
			result = new String(outSteam.toByteArray(), "utf-8");// 获取微信调用我们notify_url的返回信息
		}catch(IOException e){
			log.error("wxNotify Error", e);
		}finally{
			try {
				if (outSteam != null)
					outSteam.close();
			} catch (IOException e) {
				log.error("wxNotify Error", e);
			}
			try {
				if (inStream != null)
					inStream.close();
			} catch (IOException e) {
				log.error("wxNotify Error", e);
			}
		}
				
		
		try {
			final String xmlString = result;
			log.info("微信支付通知：" + xmlString);
			taskExecutor.submit(new Runnable() {
				@Override
				public void run() {
					try {
						if (!"".equals(xmlString)) {
							Map<String, String> mapData = XMLUtil.doXMLParse(xmlString);
							saveWxNotify(mapData);
						}
					} catch (Exception e) {
						log.error("wxNotify Error", e);
					} finally {
					}
				}
			});
			if (!"".equals(xmlString)) {
				Map<String, String> map = new HashMap<>();
				map.put("return_code", "SUCCESS");
				map.put("return_msg", "OK");
				return XmlConverUtil.maptoXml(map);
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
		return "fail";
	}

	/**
	 * alipay 跳转
	 * @return
	 */
	/*@RequestMapping(value = "/alipay/back",method = RequestMethod.GET)
	public String alipayReturn() {
		return "redirect:"+ getDomainUrl() +"/order/list";
	}*/
}
